"""
Interface for channels, since we don't use any interface python modules,
this is just for the user, to see what the required interface for a channel
is.  

The fact that the interface specifies itself as an abstract base class would
in theory prevent people from not defining all the required abstract methods
but in practice does not seem to help much.

Copyright (c) 2009 Christopher Hesse
http://code.google.com/p/python-expect/
"""

from abc import ABCMeta, abstractmethod, abstractproperty


class ChannelInterface(object):
    """
    Interface for a channel.  You do not have to inherit from this class
    to create a channel, just implement the interface.
    
    The interface for a channel is file-like, but has some slightly
    different requirements on how it works.  A Unix pipe in non-blocking
    mode would probably work directly as a channel, but of course, that
    is not portable and would not work on Windows.
    """
    __metaclass__ = ABCMeta
        
    @abstractmethod
    def fileno(self):
        """
        Returns an integer (file descriptor) that can be used with
        select.select(). This should be a socket on windows but can be
        something else, such as a pipe on POSIX operating systems.
        
        You should probably be using a Selectable object from
        expect.selectable for this purpose, unless you have a raw socket that
        you want to expect directly on.
        """
        raise NotImplementedError # pragma: no cover
    
    @abstractmethod
    def read(self, size=-1):
        """
        Read from the channel.  This method should read all available data
        and never block.  It may raise an error if it would block, as
        expectables will always wait for the channel to be readable before
        reading data.
        
        Read at most 'size' bytes from the file, or all available bytes if
        size is specified or negative.  This method may block if there are not
        'size' bytes already available on the channel.
        
        When the channel is closed, return an empty string and make sure the
        .closed property is True.
        
        An empty string can be returned normally but doesn't mean anything
        special.
        """
        raise NotImplementedError # pragma: no cover
        
    @abstractmethod
    def write(self, data):
        """
        Write to the channel. Data should always be sent, (unless the channel
        is closed at the other end) although the data does not have to be sent
        immediately.
        
        If the channel does write buffering, it should be possible to disable this
        with a 'buffering' argument to the constructor for the channel.
        
        If the channel is closed, calling this method should result in a
        ChannelClosed error.
        """
        raise NotImplementedError # pragma: no cover
        
    @abstractproperty
    def closed(self):
        """
        This property is True if the channel is closed.  If the channel is closed
        automatically
        
        If the channel is closed manually, with the .close() method, it should
        flush all writes, delete all data and mark the channel as closed.
        """
        raise NotImplementedError # pragma: no cover
        
    @abstractmethod
    def close(self):
        """
        Close the channel, flushing any data waiting to be written and marking the channel
        as closed.  A closed channel should always be marked as readable (just like a pipe).
        
        When closed, the channel flush all writes, delete all data and mark the
        channel as closed.
        """
        raise NotImplementedError # pragma: no cover
    
    @abstractmethod
    def flush(self):
        """
        Flush all data waiting to be written in the channel.
        """
        raise NotImplementedError # pragma: no cover